package com.alex.wechat.component.gateway;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.alex.insistence.web.filter.InitWapContextFilter;
import com.alex.wechat.mp.config.WxConsts;
import com.alex.wechat.mp.util.WxCryptUtilEx;

import me.chanjar.weixin.component.api.WxCompConfigStorage;
import me.chanjar.weixin.message.bean.WxXmlMessage;
import me.chanjar.weixin.message.rule.WxMessageRouter;

/**
 * 微信服务器发送给服务自身的事件推送（如取消授权通知，Ticket推送等）。
 * 此时，消息XML体中没有ToUserName字段，而是AppId字段，即公众号服务的AppId。
 * 这种系统事件推送通知（现在包括推送component_verify_ticket协议和推送取消授权通知），
 * 服务开发者收到后也需进行解密，接收到后只需直接返回字符串“success”。
 * 
 * @author Debenson
 * @since 0.1
 */
@Controller
@RequestMapping("/wxCompAuthController")
public class WxComponentAuthController {
  private static final Logger logger = LoggerFactory.getLogger(WxComponentAuthController.class);

  @Autowired
  private WxCompConfigStorage compConfig;
  @Autowired
  private WxMessageRouter wxRouter;


  /**
   * 第三方平台方可以在自己的网站:中放置“微信公众号授权”或者“小程序授权”的入口，引导公众号和小程序管理员进入授权页。授权页网址为https://mp.
   * weixin.qq.com/cgi-bin/componentloginpage?component_appid=xxxx&pre_auth_code
   * =xxxxx&redirect_uri=xxxx，该网址中第三方平台方需要提供第三方平台方appid、预授权码和回调URI
   * 
   * <br>
   * redirectUrl:
   * http://localhost:8080/cards-web/wxComponentCoreController/authorCallback.do
   * ?appid=xxxxx <br>
   * 微信会添加如下参数: auth_code=xxx&expires_in=600
   * 
   * @param appid
   *          开放平台appid
   * @param authCode
   *          授权码
   * @param expiresIn
   *          授权码的过期时间
   * @param request
   * @param response
   * @throws Exception
   */
  @RequestMapping(value = "/authorCallback")
  public ModelAndView authorCallback(
      @RequestParam(InitWapContextFilter.PARAM_WX_COMPONENT_APP_ID) String appId,
      @RequestParam("auth_code") String authCode, @RequestParam("expires_in") Integer expiresIn,
      HttpServletRequest request, HttpServletResponse response) throws Exception {
    request.setAttribute("authCode", authCode);
    return new ModelAndView("com/gomore/wechat/app/author/authorSuccess");

  }

  /**
   * 处理开放平台授权相关通知。
   * 
   * <br>
   * 当公众号对第三方平台进行授权、取消授权、更新授权后，微信服务器会向第三方平台方的授权事件接收URL （创建第三方平台时填写）推送相关通知
   * 
   * @param request
   * @param response
   * @throws Exception
   */
  @RequestMapping(value = "/authorEvent")
  public void authorEvent(HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    response.setContentType("text/html;charset=utf-8");
    response.setStatus(HttpServletResponse.SC_OK);

    if (!WxCryptUtilEx.checkSignature(compConfig.getToken(), request)) {
      // 消息签名不正确，说明不是公众平台发过来的消息
      response.getWriter().println("非法请求");
      return;
    }

    final String echoStr = request.getParameter(WxConsts.PARAM_ECHO_STR);
    if (StringUtils.isNotBlank(echoStr)) {
      // 说明是一个仅仅用来验证的请求，回显echostr
      String echoStrOut = String.copyValueOf(echoStr.toCharArray());
      response.getWriter().println(echoStrOut);
      return;
    }

    String encryptType = request.getParameter(WxConsts.PARAM_ENCRYPT_TYPE);
    if (StringUtils.isBlank(encryptType)) {
      encryptType = WxConsts.ENCRYPT_TYPE_RAW;
    }
    if (WxConsts.ENCRYPT_TYPE_RAW.equalsIgnoreCase(encryptType)) {
      // 使用明码传输
      processRawEncrypt(request, response);

    } else if (WxConsts.ENCRYPT_TYPE_AES.equalsIgnoreCase(encryptType)) {
      // 是aes加密的消息
      processAesEncrypt(request, response);

    } else {
      response.getWriter().println("不可识别的加密类型");
    }
  }

  /**
   * 处理明码请求
   * 
   * @param request
   * @param response
   * @throws Exception
   */
  private void processRawEncrypt(HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    // 解密消息
    WxXmlMessage inMessage = WxXmlMessage.fromXml(request.getInputStream());
    if (logger.isInfoEnabled()) {
      logger.info("消息解密后内容为：\n{} ", inMessage.toString());
    }

    final String outMessage = wxRouter.routeComponent(inMessage);
    if (outMessage != null) {
      if (logger.isInfoEnabled()) {
        logger.info("第三方开发平台回复内容为: {}", outMessage);
      }
      response.getWriter().write(outMessage);
    }
  }

  /**
   * 处理密文请求
   * 
   * @param request
   * @param response
   * @throws Exception
   */
  private void processAesEncrypt(HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    String msgSignature = request.getParameter("msg_signature");
    String nonce = request.getParameter("nonce");
    String timestamp = request.getParameter("timestamp");

    // 解密消息
    WxXmlMessage inMessage = WxCryptUtilEx.fromEncryptedXml(request.getInputStream(), compConfig,
        timestamp, nonce, msgSignature);
    if (logger.isInfoEnabled()) {
      logger.info("消息解密后内容为：\n{} ", inMessage.toString());
    }

    final String outMessage = wxRouter.routeComponent(inMessage);
    if (outMessage != null) {
      if (logger.isInfoEnabled()) {
        logger.info("第三方开发平台回复内容为: {}", outMessage);
      }
      response.getWriter().write(outMessage);
    }
  }

}
